﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using Newtonsoft.Json.Linq;
using TrinityAPI.Base;
using TrinityAPI.Base.Tools;
using TrinityAPI.Base.Models;

namespace JingDong.Open.Platform
{
    public class GetAccessToken : IAction
    {
        public JToken Invoke(JObject dataset, params JToken[] args)
        {
            try
            {
                var query = dataset.GetJsonObject("query");
                var constant = dataset.GetJsonObject("constant");
                var jingdongAPIConfig = constant.GetJsonObject("jingdongAPIConfig");

                var eshops = constant.GetJsonObject("eshops");
                string url = jingdongAPIConfig.GetJsonObjectStringValue("access_token_url");
                string accessTokenDir = jingdongAPIConfig.GetJsonObjectStringValue("access_token_dir");
                string appKey = jingdongAPIConfig.GetJsonObjectStringValue("app_key");
                string appSecret = jingdongAPIConfig.GetJsonObjectStringValue("app_secret");
                string state = query.GetJsonObjectStringValue("state");
                string code = query.GetJsonObjectStringValue("code");

                if (eshops == null || eshops.Count == 0 || string.IsNullOrWhiteSpace(accessTokenDir) || string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(appKey) || string.IsNullOrWhiteSpace(appSecret))
                    throw new BaseException("@1002", ""); //配置错误

                if (string.IsNullOrWhiteSpace(state))
                    throw new BaseException("@1003", ""); //参数错误

                if (string.IsNullOrWhiteSpace(code))
                    throw new BaseException("@1004", ""); //无法获得授权码

                url = string.Format(url + "?app_key={0}&app_secret={1}&grant_type=authorization_code&code={2}", appKey, appSecret, code);
                string responseString = HttpHelper.HttpWebRequest(url);
                if (string.IsNullOrWhiteSpace(responseString))
                    throw new BaseException("@1005", ""); //授权信息获取失败

                var json = JSONHelper.JsonToJObject(responseString);
                if (json == null || json.GetJsonObjectIntValue("code", 1) != 0)
                {
                    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1006");
                    var msg = json.GetJsonObjectStringValue("msg");
                    if (string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(msg))
                        throw new BaseException("@1005", ""); //授权信息获取失败
                    else
                        throw new BaseException("1006", string.Format(error, msg));//授权信息获取失败(带平台错误信息）
                }

                int expires = json.GetJsonObjectIntValue("expires_in");
                if (expires <= 0)
                {
                    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1006");
                    throw new BaseException("1006", string.Format(error, "expired"));
                }

                json["expires_time"] = DateTime.Now.AddSeconds(expires);

                string uid = json.GetJsonObjectStringValue("uid");
                string eShopCode = eshops.GetJsonObjectStringValue(uid)?.ToLower();

                if (string.IsNullOrWhiteSpace(eShopCode))
                    throw new BaseException("@1007", ""); //实际授权用户和配置不匹配

                SaveToken(Utils.GetMapPath(accessTokenDir), eShopCode, json.ToString(Newtonsoft.Json.Formatting.None));

                if (eShopCode != state)
                {
                    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1008");
                    throw new BaseException("1008", string.Format(error, eShopCode.ToUpper()));//授权账号不匹配
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Info("GetAccessToken fail: " + ex.Message);
                throw new Exception("@1001");
            }

            return null;
        }

        public static string GetToken(string eShopCode, JObject constant)
        {
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");

            var jingdongAPIConfig = constant.GetJsonObject("jingdongAPIConfig");
            int hours = jingdongAPIConfig.GetJsonObjectIntValue("refresh_token_before_hours");
            string accessTokenDir = jingdongAPIConfig.GetNecessaryJsonObjectStringValue("access_token_dir", "constant.jingdongAPIConfig");

            if (hours <= 0)
                hours = 1;

            accessTokenDir = Utils.GetMapPath(accessTokenDir);
            var accessTokenInfo = JSONHelper.JsonToJObject(Utils.LoadTextFile(string.Format("{0}\\jd_access_token_{1}.config", accessTokenDir, eShopCode.ToLower())));
            if (accessTokenInfo == null)
            {
                var emailAlter = constant.GetJsonObject("emailAlter");
                SendAlertMessage(emailAlter, jingdongAPIConfig, eShopCode);
                throw new BaseException("@0003", ""); //未授权
            }

            var now = DateTime.Now;
            var expireTime = accessTokenInfo.GetJsonObjectDateTimeValue("expires_time", now);
            var expireHours = (expireTime - now).TotalHours;
            if (expireHours <= hours)
            {
                try
                {
                    string appKey = jingdongAPIConfig.GetNecessaryJsonObjectStringValue("app_key", "constant.jingdongAPIConfig");
                    string appSecret = jingdongAPIConfig.GetNecessaryJsonObjectStringValue("app_secret", "constant.jingdongAPIConfig");
                    string refreshTokenUrl = jingdongAPIConfig.GetNecessaryJsonObjectStringValue("refresh_token_url", "constant.jingdongAPIConfig");
                    string refreshToken = accessTokenInfo.GetJsonObjectStringValue("refresh_token");
                    var refreshedTokenInfo = RefreshToken(accessTokenDir, eShopCode, refreshTokenUrl, appKey, appSecret, refreshToken);
                    accessTokenInfo = refreshedTokenInfo;
                }
                catch (Exception ex)
                {
                    LogHelper.Error("refresh token error", ex);
                    var emailAlter = constant.GetJsonObject("emailAlter");
                    SendAlertMessage(emailAlter, jingdongAPIConfig, eShopCode, expireHours);
                    if (expireHours <= 0)
                        throw new BaseException("@0004", ""); //未授权已过期
                }
            }

            return accessTokenInfo.GetJsonObjectStringValue("access_token");
        }

        private static JObject RefreshToken(string dir, string eShopCode, string refreshUrl, string apiKey, string appSecret, string refreshToken)
        {
            if (string.IsNullOrWhiteSpace(dir))
                throw new ArgumentNullException("dir");
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");
            if (string.IsNullOrWhiteSpace(refreshUrl))
                throw new ArgumentNullException("refreshUrl");

            string responseString = HttpHelper.HttpWebRequest(string.Format(refreshUrl+ "?app_key={0}&app_secret={1}&grant_type=refresh_token&refresh_token={2}", apiKey, appSecret, refreshToken));
            if (string.IsNullOrWhiteSpace(responseString))
                throw new Exception("refresh token no response");

            var json = JSONHelper.JsonToJObject(responseString);

            if (json == null)
                throw new Exception("refresh token no json response");

            if (json.GetJsonObjectIntValue("code", 1) != 0)
            {
                var msg = json.GetJsonObjectStringValue("msg");
                throw new Exception(string.Format("refresh token code={0},msg={1}", json.GetJsonObjectValue("code")?.ToString(), msg));
            }

            int expires = json.GetJsonObjectIntValue("expires_in");
            if (expires <= 0)
                throw new Exception("refresh token no 'expires_in' in json response");

            json["expires_time"] = DateTime.Now.AddSeconds(expires);

            SaveToken(dir, eShopCode, json.ToString(Newtonsoft.Json.Formatting.None));

            return json;
        }

        private static void SaveToken(string dir, string eShopCode, string content)
        {
            if (string.IsNullOrWhiteSpace(dir))
                throw new ArgumentNullException("dir");
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");
            if (string.IsNullOrWhiteSpace(content))
                throw new ArgumentNullException("content");

            FileStream fileStream = null;
            StreamWriter streamWriter = null;
            try
            {
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                fileStream = new FileStream(string.Format("{0}\\jd_access_token_{1}.config", dir, eShopCode.ToLower()), FileMode.Create);
                streamWriter = new StreamWriter(fileStream, Encoding.Default);
                streamWriter.Write(content);
                streamWriter.Flush();
                streamWriter.Close();
                streamWriter.Dispose();
                fileStream.Close();
                fileStream.Dispose();
                streamWriter = null;
                fileStream = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (streamWriter != null)
                {
                    streamWriter.Flush();
                    streamWriter.Close();
                    streamWriter.Dispose();
                }
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                }
            }
        }

        private static void SendAlertMessage(JObject config, JObject apiConfig, string eShopCode, double? hours = null)
        {
            if (config == null || string.IsNullOrWhiteSpace(eShopCode))
                return;
            string url = config.GetJsonObjectStringValue("url");
            string apiKey = config.GetJsonObjectStringValue("apiKey");
            string secretKey = config.GetJsonObjectStringValue("secretKey");
            string receiver = config.GetJsonObjectStringValue("receiver");
            string cc = config.GetJsonObjectStringValue("cc");
            string bcc = config.GetJsonObjectStringValue("bcc");
            if (string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(receiver))
                return;

            string appKey = apiConfig.GetJsonObjectStringValue("app_key");
            string callbackUrl = apiConfig.GetJsonObjectStringValue("access_token_callback_url");
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(o =>
            {
                try
                {
                    var eShopName = eShopCode.ToUpper();
                    var requestTime = DateTime.UtcNow.ToString("yyyyMMddHHmmss");

                    var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                    byte[] result = md5.ComputeHash(Encoding.UTF8.GetBytes(apiKey + requestTime + secretKey));

                    var sb = new StringBuilder();

                    for (int i = 0; i < result.Length; i++)
                    {
                        sb.Append(result[i].ToString("X2"));
                    }
                    string authKey = sb.ToString();

                    string subject = string.Format("{0} 京东开放平台请求授权", eShopName);
                    string message = string.Format("{0} 京东开放平台未授权。请授权：", eShopName);

                    if (hours.HasValue)
                    {
                        string flag = hours > 0 ? "将在" + DateTime.Now.AddHours(hours.Value).ToString("yyyy-MM-dd HH:mm") : "已经";
                        subject = string.Format("{0} 京东开放平台授权{1}过期", eShopName, hours.Value > 0 ? "即将" : "已经");
                        message = string.Format("{0} 京东开放平台授权{1}过期。请及时重新授权：", eShopName, flag);
                    }

                    string body = "<style>/*TableHead*/#table-5theadth{background-color:rgb(156,186,95);color:#fff;border-bottom-width:0;}/*HeadingandColumnStyle*/#table-5tr,#table-5th,#table-5td{border-width:1px;border-style:solid;border-color:rgb(156,186,95);}/*Paddingandfontstyle*/#table-5th{padding:5px10px;font-size:12px;font-family:Verdana;font-weight:bold;}#table-5td{padding:5px10px;}#table-5tabletd{white-space:nowrap;font-size:12px;font-family:Verdana;border:none;}body{font-size:15px;font-family:Verdana;font-weight:100;}</style><body>Dear Team:<br/><br/>" + message + "<br/><br/>授权网址：<br/>https://open-oauth.jd.com/oauth2/to_login?app_key="+ appKey + "&response_type=code&redirect_uri="+ System.Web.HttpUtility.UrlEncode(callbackUrl) + "&state=" + eShopCode.ToLower() + "&scope=snsapi_base<br/><br/><font color='red'>注意：授权后，如果修改密码，需要重新授权！</font><br/>(这是系统生成的电子邮件，请不要回复。)<br/><br/></body>";

                    var request = new JObject();
                    request.Add("apiKey", apiKey);
                    request.Add("authKey", authKey);
                    request.Add("requestTime", requestTime);
                    request.Add("subject", subject);
                    request.Add("message", body);
                    request.Add("receiver", receiver);
                    request.Add("cc", cc);
                    request.Add("bcc", bcc);

                    var requestString = request.ToString(Newtonsoft.Json.Formatting.None);

                    HttpHelper.HttpWebRequest(url, requestString, "POST", 10000, new Dictionary<string, string>() { { "Content-Type", "application/json" } });
                }
                catch (Exception ex)
                {
                    LogHelper.Error("Send authorization email error", ex);
                }
            }));
        }
    }
}
